﻿using System;
using System.Globalization;
using System.Web;
using System.Web.Mvc;
using System.Linq;
using System.Text.RegularExpressions;
using Orchard;
using Orchard.Localization;
using Orchard.Themes;
using OrchardPo.Services;

namespace OrchardPo.Controllers
{
    [Themed]
    public class LocalizationController : Controller {
        private readonly ILocalizationService _localizationService;
        private readonly Regex _moduleNameExpression = new Regex(@"^(\w+\.)*\w+$", RegexOptions.Singleline);

        public Localizer T { get; set; }
        public IOrchardServices Services { get; set; }

        public LocalizationController(
            ILocalizationService localizationService,
            IOrchardServices services) {
            _localizationService = localizationService;
            Services = services;
            T = NullLocalizer.Instance;
        }

        public ActionResult Index() {
            var model = _localizationService.GetExistingTranslationCultures();
            model.CanUpload = Services.Authorizer.Authorize(Permissions.UploadTranslation);
            return View(model);
        }

        public ActionResult DetailsRoot(string culture) {
            new CultureInfo(culture); // blow up if not valid culture
            return DetailsPrivate("App_Data/Localization/{0}/orchard.root.po", culture);
        }

        public ActionResult DetailsCore(string culture) {
            new CultureInfo(culture); // blow up if not valid culture
            return DetailsPrivate("Core/App_Data/Localization/{0}/orchard.core.po", culture);
        }

        public ActionResult DetailsModule(string module, string culture) {
            new CultureInfo(culture); // blow up if not valid culture
            ValidateModuleName(module);
            return DetailsPrivate("Modules/" + module + "/App_Data/Localization/{0}/orchard.module.po", culture);
        }

        public ActionResult DetailsTheme(string theme, string culture) {
            new CultureInfo(culture); // blow up if not valid culture
            ValidateModuleName(theme);
            return DetailsPrivate("Themes/" + theme + "/App_Data/Localization/{0}/orchard.theme.po", culture);
        }

        public ActionResult Details(string path) {
            string group;
            string culture;
            ExtractCultureAndGroup(path, out group, out culture);
            return DetailsPrivate(group, culture);
        }

        private static string ExtractModule(string path) {
            if (path.StartsWith("Modules/", StringComparison.OrdinalIgnoreCase)) {
                var nextSlash = path.IndexOf('/', 8);
                if (nextSlash != -1) {
                    return path.Substring(8, nextSlash - 8);
                }
            }
            return null;
        }

        private static string ExtractTheme(string path) {
            if (path.StartsWith("Themes/", StringComparison.OrdinalIgnoreCase)) {
                var nextSlash = path.IndexOf('/', 7);
                if (nextSlash != -1) {
                    return path.Substring(7, nextSlash - 7);
                }
            }
            return null;
        }

        private static void ExtractCultureAndGroup(string path, out string group, out string culture) {
            culture = null;
            group = null;
            if (path.EndsWith(".po")) {
                var lastSlash = path.LastIndexOf('/');
                if (lastSlash != -1) {
                    var slashBeforeThat = path.LastIndexOf('/', lastSlash - 1);
                    if (slashBeforeThat != -1) {
                        culture = path.Substring(slashBeforeThat + 1, lastSlash - slashBeforeThat - 1);
                        group = path.Replace(culture, "{0}");
                    }
                }
            }
            else {
                culture = path;
            }
        }

        private void ValidateModuleName(string name) {
            if (name == null || !_moduleNameExpression.IsMatch(name)) {
                throw new HttpException(404, "Not found");
            }
        }

        private ActionResult DetailsPrivate(string group, string culture) {
            if (culture == null) throw new HttpException(404, "Not found");
            new CultureInfo(culture); // Throws if invalid culture
            var model = _localizationService.GetGroupDetailsViewModel(culture, group);
            // Post-process the results from the service to show routes instead of paths
            foreach (var otherGroup in model.Groups) {
                otherGroup.Url = GetRouteFromPath(otherGroup.Path, culture);
            }
            model.CanTranslate = Services.Authorizer.Authorize(Permissions.Translate) &&
                                 _localizationService.IsCultureAllowed(culture);
            return View("Details", model);
        }

        private string GetRouteFromPath(string path, string culture) {
            if (path.StartsWith("App_Data/", StringComparison.OrdinalIgnoreCase)) {
                // Root
                return Url.Action("DetailsRoot", new {culture});
            }
            if (path.StartsWith("Core/", StringComparison.OrdinalIgnoreCase)) {
                return Url.Action("DetailsCore", new {culture});
            }
            if (path.StartsWith("Modules", StringComparison.OrdinalIgnoreCase)) {
                return Url.Action("DetailsModule", new {module = ExtractModule(path), culture});
            }
            if (path.StartsWith("Themes", StringComparison.OrdinalIgnoreCase)) {
                return Url.Action("DetailsTheme", new { theme = ExtractTheme(path), culture });
            }
            return path;
        }

        [HttpPost]
        public ActionResult Upload() {
            if (!Services.Authorizer.Authorize(
                Permissions.UploadTranslation, T("You are not allowed to upload translations.")))
                return new HttpUnauthorizedResult();

            foreach (var file in
                from string fileName in Request.Files
                select Request.Files[fileName]) {
                _localizationService.UnzipPostedFileToDatabase(file);
            }
            return RedirectToAction("Index");
        }

        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Update(int id, string culture, string value) {
            if (!Services.Authorizer.Authorize(
                Permissions.Translate, T("You are not allowed to update translations.")))
                return new HttpUnauthorizedResult();

            if (!_localizationService.IsCultureAllowed(culture)) {
                return new HttpUnauthorizedResult();
            }
            _localizationService.UpdateTranslation(id, culture, value);
            return new JsonResult {Data = value};
        }

        [HttpPost]
        public ActionResult Remove(int id, string culture) {
            if (!Services.Authorizer.Authorize(
                Permissions.Translate, T("You are not allowed to delete translations.")))
                return new HttpUnauthorizedResult();

            if (!_localizationService.IsCultureAllowed(culture)) {
                return new HttpUnauthorizedResult();
            }
            _localizationService.RemoveTranslation(id, culture);
            return new JsonResult {Data = null};
        }

        public ActionResult Download(string culture) {
            var cachePath = Server.MapPath("~/Modules/OrchardPo/Content/orchard." + culture + ".po.zip");
            if (System.IO.File.Exists(cachePath) &&
                DateTime.Now - System.IO.File.GetLastWriteTime(cachePath) < TimeSpan.FromDays(1)) {
                return new FilePathResult(cachePath, "application/zip");
            }
            byte[] zipBytes = _localizationService.GetZipBytes(culture);
            System.IO.File.WriteAllBytes(cachePath, zipBytes);
            return new FileContentResult(zipBytes, "application/zip") {
                FileDownloadName = "orchard." + culture + ".po.zip"
            };
        }

        // Maintenance action
        public ActionResult Cleanup() {
            if (!Services.Authorizer.Authorize(
                Permissions.UploadTranslation, T("You are not allowed to do maintenance operations on translation tables.")))
                return new HttpUnauthorizedResult();

            _localizationService.Cleanup();
            return RedirectToAction("Index");
        }
    }
}
